/**************************************************************************** 
 * Meta，一个轻量级的静态分区Hypervisor 
 *
 * 版权(c) , 2022-
 *
 * 作者:
 *      Tupelo Shen <shenwanjiang2013@163.com>
 *
 * Meta是一个自由软件，你可以在遵循GNU GPLv2协议的条件下，重新修改并发布它。
 *
 ***************************************************************************/

#include <meta.h>
#include <console.h>

#include <platform.h>
#include <cpu.h>
#include <mem.h>
#include <fences.h>
#include <spinlock.h>

/**
 * UART串口数据结构：
 *   (1) 保存在`.devices`自定义数据段中（对应设备内存）
 *   (2) volative，保证编译不会优化，数据每次从内存中重新读取，不会从Cache中读取
 *   (3) UART对应的设备内存按照page页大小对齐
 */
volatile meta_uart_t uart
    __attribute__((section(".devices"), aligned(PAGE_SIZE)));

/* UART准备好标志 */
bool ready = false;

/* 保护UART的自旋锁 */
static spinlock_t print_lock = SPINLOCK_INITVAL;

/****************************************************************************
 ****                         公共函数定义                               ****
 ****************************************************************************/
/**
 * 功能描述: hypervisor初始化函数，也是主入口
 * 入口参数: cpu_id:        CPU核ID
 *           load_addr:     加载物理地址
 *           config_addr:   配置信息的物理地址
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
void console_init(void)
{
    /**console绑定的UART对应的物理基地址;
     * （需要在./src/platform目录下添加硬件平台的描述，比如UART的基地址
     */
    if((platform.console.base & PAGE_OFFSET_MASK) != 0) {
        WARNING("console base must be page aligned");
    }

    /* 将物理地址与虚拟地址建立映射，并将其保存到CPU核对应的地址空间的页表中 */
    mem_map_dev(&cpu.as, (vaddr_t)&uart, platform.console.base,
                NUM_PAGES(sizeof(uart)));

    /* 保证设备内存正确保存到页表中 */
    fence_sync_write();

    /* 串口初始化并并使能串口 */
    uart_init(&uart);
    uart_enable(&uart);

    /* 串口准备好标志 */
    ready = true;
}

/**
 * 功能描述: 往console写数据
 * 入口参数: str:       待写入UART的字符串
 * 出口参数: void
 * 返 回 值: void
 * 修改日期: 2022-09-21
 */
void console_write(char const* const str)
{
    if (!ready) return;
    spin_lock(&print_lock);
    uart_puts(&uart, str);
    spin_unlock(&print_lock);
}
